{{>licenseInfo}}
import expect from 'expect.js';
import sinon from 'sinon';
import { join } from 'path';

const modulePath = join(process.cwd(), 'src', '{{invokerPackage}}', 'index.js');
const {{moduleName}} = await import(modulePath);

let instance;
let sandbox;
const testEndpoint = 'https://localhost:3000';
const testAccessToken = "testAccessToken";

// Helper function to generate random test data
function generateMockData(dataType, isArray = false) {
  if (!dataType) return {};

  // Handle array types
  if (isArray) {
    return [generateMockData(dataType), generateMockData(dataType)];
  }

  switch(dataType) {
    case 'String':
      return 'mock-' + Math.random().toString(36).substring(2, 10);
    case 'Number':
      return Math.floor(Math.random() * 1000);
    case 'Boolean':
      return Math.random() > 0.5;
    case 'Date':
      return new Date().toISOString();
    default:
      try {
        const ModelClass = {{moduleName}}[dataType];
        if (ModelClass) {
          const instance = Object.create(ModelClass.prototype);
          return instance;
        }
      } catch (e) {
        console.error("Error creating instance of", dataType);
        return {};
      }
      return {};
  }
}

// Generate mock requests and responses for each operation
{{#operations}}
{{#operation}}
const mock{{operationId}}Data = {
  request: {
    {{#allParams}}
    {{#required}}
    '{{paramName}}': {{#isBodyParam}}generateMockData('{{dataType}}'{{#isArray}}, true{{/isArray}}){{/isBodyParam}}{{^isBodyParam}}{{#isArray}}generateMockData('{{items.dataType}}', true){{/isArray}}{{^isArray}}generateMockData('{{dataType}}'){{/isArray}}{{/isBodyParam}}{{^-last}},{{/-last}}
    {{/required}}
    {{/allParams}}
  },
  response: {
    {{#responses}}
    {{#-first}}
    {{#schema}}
    data: generateMockData('{{returnBaseType}}'{{#returnTypeIsArray}}, true{{/returnTypeIsArray}}),
    {{/schema}}
    statusCode: {{code}},
    {{/-first}}
    {{/responses}}
    headers: {}
  }
};
{{/operation}}
{{/operations}}

describe('{{classname}}', () => {
  beforeEach(() => {
    sandbox = sinon.createSandbox();
    const apiClientInstance = new {{moduleName}}.ApiClient(testEndpoint);
    apiClientInstance.applyXAmzAccessTokenToRequest(testAccessToken);
    sandbox.stub(apiClientInstance, 'callApi');
    instance = new {{moduleName}}.{{classname}}(apiClientInstance);
  });

  afterEach(() => {
    sandbox.restore();
  });

  {{#operations}}
  {{#operation}}
  describe('{{operationId}}', () => {
    {{^operationIdsetAppointmentFulfillmentData}}
    it('should successfully call {{operationId}}', async () => {
      instance.apiClient.callApi.resolves(mock{{operationId}}Data.response);

      {{#hasParams}}
      const params = [
        {{#allParams}}
        {{#required}}
        mock{{operationId}}Data.request['{{paramName}}']{{^-last}},{{/-last}}
        {{/required}}
        {{/allParams}}
      ];
      const data = await instance.{{operationId}}(...params);
      {{/hasParams}}
      {{^hasParams}}
      const data = await instance.{{operationId}}();
      {{/hasParams}}

      {{#responses}}
      {{#-first}}
      {{#schema}}
      expect(data instanceof {{#isPrimitiveType}}{{returnBaseType}}{{/isPrimitiveType}}{{^isPrimitiveType}}{{moduleName}}.{{returnBaseType}}{{/isPrimitiveType}}).to.be.true;
      expect(data).to.equal(mock{{operationId}}Data.response.data);
      {{/schema}}
      {{^schema}}
      expect(data).to.be.undefined;
      {{/schema}}
      {{/-first}}
      {{/responses}}
    });
    {{/operationIdsetAppointmentFulfillmentData}}

    it('should successfully call {{operationId}}WithHttpInfo', async () => {
      instance.apiClient.callApi.resolves(mock{{operationId}}Data.response);

      {{#hasParams}}
      const params = [
        {{#allParams}}
        {{#required}}
        mock{{operationId}}Data.request['{{paramName}}']{{^-last}},{{/-last}}
        {{/required}}
        {{/allParams}}
      ];
      const response = await instance.{{operationId}}WithHttpInfo(...params);
      {{/hasParams}}
      {{^hasParams}}
      const response = await instance.{{operationId}}WithHttpInfo();
      {{/hasParams}}

      expect(response).to.have.property('statusCode');
      expect(response.statusCode).to.equal(mock{{operationId}}Data.response.statusCode)
      expect(response).to.have.property('headers');
      {{#responses}}
      {{#-first}}
      {{#schema}}
      expect(response).to.have.property('data');
      expect(response.data).to.equal(mock{{operationId}}Data.response.data)
      {{/schema}}
      {{/-first}}
      {{/responses}}
    });

    it('should handle API errors', async () => {
      const errorResponse = {
        errors: new Error('Expected error to be thrown'),
        statusCode: 400,
        headers: {}
      };
      instance.apiClient.callApi.rejects(errorResponse);

      try {
        {{#hasParams}}
        const params = [
          {{#allParams}}
          {{#required}}
          mock{{operationId}}Data.request['{{paramName}}']{{^-last}},{{/-last}}
          {{/required}}
          {{/allParams}}
        ];
        await instance.{{operationId}}(...params);
        {{/hasParams}}
        {{^hasParams}}
        await instance.{{operationId}}();
        {{/hasParams}}
        throw new Error('Expected error to be thrown');
      } catch (error) {
        expect(error).to.exist;
        expect(error.statusCode).to.equal(400);
      }
    });
  });
  {{/operation}}
  {{/operations}}

  describe('constructor', () => {
    it('should use default ApiClient when none provided', () => {
      const defaultInstance = new {{moduleName}}.{{classname}}();
      expect(defaultInstance.apiClient).to.equal({{moduleName}}.ApiClient.instance);
    });

    it('should use provided ApiClient', () => {
      const customClient = new {{moduleName}}.ApiClient();
      const customInstance = new {{moduleName}}.{{classname}}(customClient);
      expect(customInstance.apiClient).to.equal(customClient);
    });
  });
});
